/**
 * 
 */
package co.fitstart.mobile.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.utils.CollectionUtils;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;

import co.fitstart.entity.coach.CoachRestCalendar;
import co.fitstart.entity.coach.CoachRestType;
import co.fitstart.entity.coach.CoachStudentRelation;
import co.fitstart.entity.course.Course;
import co.fitstart.entity.course.CourseType;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.course.round.RoundStatus;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.course.schedule.CourseScheduleCancelRecord;
import co.fitstart.entity.course.schedule.ScheduleStatus;
import co.fitstart.entity.order.OrderStatus;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.principal.PrincipalMessage;
import co.fitstart.entity.site.RoomScheduleRecord;
import co.fitstart.entity.site.RoomType;
import co.fitstart.entity.site.Site;
import co.fitstart.entity.user.Student;
import co.fitstart.mobile.dao.support.ScheduleDaoSupport;
import co.fitstart.mobile.entity.appointment.Appointment;
import co.fitstart.mobile.entity.appointment.AppointmentPrepare;
import co.fitstart.mobile.entity.appointment.CoachPrepare;
import co.fitstart.mobile.entity.appointment.RoomPrepare;
import co.fitstart.mobile.handler.SmsSender;
import co.fitstart.mobile.handler.TencentHandler;
import co.fitstart.mobile.service.AppointmentService;

/**
 * @author downpour
 *
 */
@Service("appointmentService")
public class AppointmentServiceImpl implements AppointmentService {

    @Autowired
    private ScheduleDaoSupport scheduleDaoSupport;
    
    @Autowired
    private GenericDaoSupport genericDaoSupport;
    
    @Autowired
    private TencentHandler tencentHandler;
    
    @Autowired
    private SmsSender smsSender;
    
    /* (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#validate(co.fitstart.entity.user.Student, co.fitstart.entity.course.round.CourseRound)
     */
    public void validate(Student student, CourseRound courseRound) {
        
        CourseType courseType = courseRound.getCourseType();
        
        // check course type
        if(!courseType.supportAppointment()) {
            throw new BusinessException("该课程不支持预约");
        }

        // check courseRound matches student or not
        if(student == null || !courseRound.matches(student)) {
            throw new BusinessException("学员课程数据有误");
        }
        
    }

    /* (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#prepare(co.fitstart.entity.course.round.CourseRound, co.fitstart.entity.site.Site, java.util.Date)
     */
    public AppointmentPrepare prepare(CourseRound courseRound, Site site, Date day) {
        
        // new appointment prepare with courseRound, site, day
        AppointmentPrepare appointmentPrepare = new AppointmentPrepare(courseRound, site, day);
        day = appointmentPrepare.getCurrentDay();
        boolean experience = (courseRound.getCourseType().isIndividual() && courseRound.getCourse().isExperience());
        
        // List roomSchedule stat by site, roomType, day
        Map<String, Integer> commomLimits = scheduleDaoSupport.dayhourRoomLimits(site, RoomType.COMMON, day, appointmentPrepare.getAppointStartTime(), appointmentPrepare.getAppointEndTime(), experience); 
        Map<String, Integer> roomLimits = scheduleDaoSupport.dayhourRoomLimits(site, RoomType.ROOM, day, appointmentPrepare.getAppointStartTime(), appointmentPrepare.getAppointEndTime(), experience);
        
        // List coach amount stat by site day
//        Map<String, Integer> scheduleAmount = scheduleDaoSupport.dayhourScheduleCoachAmount(site, day);
//        Map<String, Integer> restAmount = scheduleDaoSupport.dayhourRestCoachAmount(site, day);
        
        // get total coach by site
//        int coachAmount = scheduleDaoSupport.listCoach(site).size();
        
        // get remain coaches
        Map<String, Integer> remainCoaches = scheduleDaoSupport.dayhourRemainCoach(site, day, appointmentPrepare.getAppointStartTime(), appointmentPrepare.getAppointEndTime(), experience);
        
        appointmentPrepare.init(roomLimits, commomLimits, remainCoaches);
        
        return appointmentPrepare;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#prepareIndividual(co.fitstart.mobile.entity.appointment.AppointmentPrepare, co.fitstart.entity.course.round.CourseRound, co.fitstart.entity.principal.Principal)
     */
    public AppointmentPrepare prepareIndividual(AppointmentPrepare appointmentPrepare, CourseRound courseRound, Principal coach) {
    	
        Date day = appointmentPrepare.getCurrentDay();
        
        Map<String, CoachRestCalendar> coachRestCalendars = scheduleDaoSupport.mapCoachResrCalendar(coach, day);
        Map<String, CourseSchedule> coachCourseSchedules = scheduleDaoSupport.mapCourseSchedule(coach, day);
        
        appointmentPrepare.individualInit(coachRestCalendars, coachCourseSchedules);
        
        return appointmentPrepare;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#prepareYear(co.fitstart.mobile.entity.appointment.AppointmentPrepare, co.fitstart.entity.course.schedule.CourseSchedule)
     */
    public AppointmentPrepare prepareYear(AppointmentPrepare appointmentPrepare, CourseSchedule courseSchedule) {
        Principal coach = courseSchedule.getCoach();
        Date day = appointmentPrepare.getCurrentDay();
        
        Map<String, CoachRestCalendar> coachRestCalendars = scheduleDaoSupport.mapCoachResrCalendar(coach, day);
        Map<String, CourseSchedule> coachCourseSchedules = scheduleDaoSupport.mapCourseSchedule(coach, day);
        
        appointmentPrepare.individualInit(coachRestCalendars, coachCourseSchedules);
        
        return appointmentPrepare;
    }
    
    /**
     * 
     * @param student
     * @param appointment
     * @param selectedScheduleId
     * @param experience
     */
    private void validateStudentTime(Student student, Appointment appointment, Long selectedScheduleId, boolean experience) {
        
        Date day = appointment.getDay();
        String hour = appointment.getHour();
        
        Date dayhour = DateUtils.parseDate(DateFormatUtils.format(day, "yyyy-MM-dd") + " " + hour + ":00", "yyyy-MM-dd HH:mm:ss");
        String beforeHour = DateFormatUtils
                .format(org.apache.commons.lang3.time.DateUtils.addHours(dayhour, -1), "HH:mm");
        String beforeHalfhour = DateFormatUtils
                .format(org.apache.commons.lang3.time.DateUtils.addMinutes(dayhour, -30), "HH:mm");
        String afterHalfhour = DateFormatUtils
                .format(org.apache.commons.lang3.time.DateUtils.addMinutes(dayhour, 30), "HH:mm");
        String[] hours;
        String[] experienceHours;
        
        if(experience) {
            String afterHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(dayhour, 1),"HH:mm");
            
            hours = new String[]{beforeHalfhour, hour, afterHalfhour, afterHour};
            experienceHours = new String[]{beforeHour, beforeHalfhour, hour, afterHalfhour, afterHour};
        } else {
            hours = new String[]{beforeHalfhour, hour, afterHalfhour};
            experienceHours = new String[]{beforeHour, beforeHalfhour, hour, afterHalfhour};
        }
        
        List<CourseSchedule> schedules = scheduleDaoSupport.listUnfinishedCourseSchedule(student, day, hours, false, selectedScheduleId);
        List<CourseSchedule> experienceSchedules = scheduleDaoSupport.listUnfinishedCourseSchedule(student, day, experienceHours, true, selectedScheduleId);
        
        if(!schedules.isEmpty() || !experienceSchedules.isEmpty()) {
            throw new BusinessException("所选时间您已有其他课程!");
        }
    }
    
    /**
     * 
     * @param courseRound
     * @param date
     * @param hour
     */
    private void validateCourseTime(CourseRound courseRound, Date date, String hour) {
        
        Date currentDateTime = DateUtils.parseDate(DateFormatUtils.format(date, "yyyy-MM-dd") + " " + hour + ":00", "yyyy-MM-dd HH:mm:ss");
        
        // compare date & today, is date small than day throw exception 
        if(currentDateTime.before(new Date())) {
            throw new BusinessException("所选时间小于当前时间，请重新选择");
        }
        
        // validate course appoint hour
        Date compareStartTime = DateUtils.parseDate(DateFormatUtils.format(date, "yyyy-MM-dd") + " " + courseRound.getAppointStartTime() + ":00", "yyyy-MM-dd HH:mm:ss");
        Date compareEndTime = DateUtils.parseDate(DateFormatUtils.format(date, "yyyy-MM-dd") + " " + courseRound.getAppointEndTime() + ":00", "yyyy-MM-dd HH:mm:ss");
        if(currentDateTime.before(compareStartTime) || currentDateTime.after(compareEndTime)) {
            throw new BusinessException("所选时间不在该课程可预约时间，请重新选择");
        }
        
        // 判断所选时间是否在过期时间之后
        Date effectiveEndDate = courseRound.getEffectiveEnd();
        if(effectiveEndDate != null && currentDateTime.after(effectiveEndDate)) {
            throw new BusinessException("所选时间该课程已过期");
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#validateAppointment(co.fitstart.entity.user.Student, co.fitstart.entity.course.round.CourseRound, co.fitstart.mobile.entity.appointment.Appointment)
     */
    public void validateAppointment(Student student, CourseRound courseRound, Appointment appointment) {
        
        // step1 validate appointment
        if(!appointment.isValid()) {
            throw new BusinessException("约课参数有误");
        }
        
        // step2 validate student & courseRound matches or not
        this.validate(student, courseRound);
        
        // step3 validate course time
        this.validateCourseTime(courseRound, appointment.getDay(), appointment.getHour());
        
        // step3 validate student's day hour with params student & appoint
        this.validateStudentTime(student, appointment, null, courseRound.getCourse().isExperience());
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#validateAppointment(co.fitstart.entity.user.Student, co.fitstart.entity.course.round.CourseRound, co.fitstart.mobile.entity.appointment.Appointment, java.lang.Long)
     */
    public void validateAppointment(Student student, CourseRound courseRound, Appointment appointment,
            Long selectedScheduleId) {
        
        // step1 validate appointment
        if(!appointment.isValid()) {
            throw new BusinessException("约课参数有误");
        }
        
        // step2 validate student & courseRound matches or not
        this.validate(student, courseRound);
        
        // step3 validate course time
        this.validateCourseTime(courseRound, appointment.getDay(), appointment.getHour());
        
        // step3 validate student's day hour with params student & appoint
        this.validateStudentTime(student, appointment, selectedScheduleId, courseRound.getCourse().isExperience());
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#generateSiteData(co.fitstart.entity.course.round.CourseRound, co.fitstart.entity.site.Site, java.util.Date, java.lang.String)
     */
    @SuppressWarnings("unchecked")
    public RoomPrepare prepareRoom(CourseRound courseRound, Site site, Date day, String hour) {
        boolean experience = (courseRound.getCourseType().isIndividual() && courseRound.getCourse().isExperience());
        
        RoomPrepare roomPrepare = new RoomPrepare(site);
        
        // get total coach
        List<Principal> coaches = scheduleDaoSupport.listCoach(site, day);
        int totalCoach = coaches.size(), remainCoach = totalCoach;
        
        // 所有非空闲教练
        Set<Principal> busyCoaches = new HashSet<Principal>();
        
        // calculate RoomType.ROOM remain
        int roomRemain = site.getRoomLimit().getRoomLimit();
        
        roomRemain = roomRemain - scheduleDaoSupport.countUsingRoom(site, RoomType.ROOM, day, hour, experience);
        // 列出所有该时间私人区域上课课程
        List<CourseSchedule> roomSchedules = scheduleDaoSupport.listCourseSchedule(site, RoomType.ROOM, day, hour);
        if(!roomSchedules.isEmpty()) {
            busyCoaches.addAll(CollectionUtils.extractPropertyList(roomSchedules, "coach"));
        }
        
        // 列出所有该时间休息的教练
        busyCoaches.addAll(scheduleDaoSupport.listDayhourRestCoach(coaches, day, hour));
        
        // calculate RoomType.COMMON remain
        int commonRemain = site.getRoomLimit().getCommonLimit();
        
        commonRemain = commonRemain - scheduleDaoSupport.countUsingRoom(site, RoomType.COMMON, day, hour, experience);
        // 列出所有该时间公共区域上课课程
        List<CourseSchedule> commonSchedules = scheduleDaoSupport.listCourseSchedule(site, RoomType.COMMON, day, hour);
        if(!commonSchedules.isEmpty()) {
            busyCoaches.addAll(CollectionUtils.extractPropertyList(commonSchedules, "coach"));
        }
        
        remainCoach = remainCoach - busyCoaches.size();
        
        roomRemain = roomRemain > 0 ? roomRemain : 0;
        remainCoach = remainCoach > 0 ? remainCoach : 0;
        commonRemain = commonRemain > 0 ? commonRemain : 0;
        roomPrepare.roomRemain(roomRemain < remainCoach ? roomRemain : remainCoach);
        roomPrepare.commonRemain(commonRemain < remainCoach ? commonRemain : remainCoach);
        
        if(!roomPrepare.isValid()) {
            throw new BusinessException("所选时间无房间空闲");
        }
        
        return roomPrepare;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#validateCoach(co.fitstart.entity.principal.Principal, java.util.Date, java.lang.String, boolean, java.lang.Long)
     */
    public void validateCoach(Principal coach, Date day, String hour, boolean experience, Long scheduleId) {
        
        Date date = DateUtils.parseDate(DateFormatUtils.format(day, "yyyy-MM-dd") + " " + hour + ":00", "yyyy-MM-dd HH:mm:ss");
        
        String beforeHalfhour = DateFormatUtils
                .format(org.apache.commons.lang3.time.DateUtils.addMinutes(date, -30), "HH:mm");
        String afterHalfhour = DateFormatUtils
                .format(org.apache.commons.lang3.time.DateUtils.addMinutes(date, 30), "HH:mm");
        
        String[] hours;
        
        if(experience) {
            String afterHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(date, 1),
                    "HH:mm");
            
            hours = new String[]{beforeHalfhour, hour, afterHalfhour, afterHour};
        } else {
            hours = new String[]{beforeHalfhour, hour, afterHalfhour};
        }
        
        List<CourseSchedule> schedules = scheduleDaoSupport.listCourseSchedule(coach, day, hours, scheduleId);
        
        if(!schedules.isEmpty()) {
            throw new BusinessException("所选教练已有其他课程");
        } else {
            //检查一小时前是否有体验课，且不是当前课时
            CourseSchedule schedule = scheduleDaoSupport.loadCourseSchedule(coach, day, hour);
            
            if(schedule!= null && schedule.getId() != scheduleId) {
                throw new BusinessException("所选教练已有其他课程");
            }
        }
        
        // validate coach rest time
        String[] restHours = new String[]{hour, DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addMinutes(date, 30), "HH:mm")};
        CoachRestCalendar coachRestCalendar = scheduleDaoSupport.loadCoachRestCalendar(coach, day, restHours);
        if(coachRestCalendar != null) {
            throw new BusinessException("所选教练在休息时间");
        }
        
//        // validate coach time
//        String[] hours = new String[]{DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(date, -1), "HH:mm"), DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(date, 1), "HH:mm")};
//        CourseSchedule schedule = scheduleDaoSupport.loadCourseSchedule(coach, day, hours);
//        if(schedule != null) {
//            throw new BusinessException("所选教练所选时间前/后半小时已有课程，不可选择");
//        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#validateYearCourse(co.fitstart.entity.user.Student, co.fitstart.entity.course.round.CourseRound, java.util.Date, java.lang.String)
     */
    public void validateYearCourse(Student student, CourseRound courseRound, Date day, String hour) {
        
    	// 用户一天只能预约一次
        CourseSchedule courseSchedule = scheduleDaoSupport.loadCourseSchedule(courseRound, student, day);
        if(courseSchedule != null) {
            throw new BusinessException("本课程今日已有预约");
        }
        
        // 校验用户在本年卡是否有失信
        if(courseRound.isDishonesty()) {
            // put monday and sunday as parameter
               Date startDate = DateUtils.getFirstWeekDay(new Date());
               Date endDate = DateUtils.addDays(startDate, 6);
               
               List<CourseSchedule> thisWeekSchedules = scheduleDaoSupport.listCourseSchedule(student, courseRound, startDate, endDate);
               if(org.apache.commons.collections.CollectionUtils.isNotEmpty(thisWeekSchedules)) {
                   throw new BusinessException("失信用户，一周只能预约一节课程");
               }
           }
        
        // 校验教练的休息时间
        
        // 校验教练的上课时间
        
    }
    
    /**
     * 
     * @param coaches
     * @param day
     * @param hour
     * @param experience
     * @return
     */
    private List<CoachPrepare> filterCoaches(List<Principal> coaches, Date day, String hour, boolean experience) {
        
        List<CoachPrepare> fiterCoaches = new ArrayList<CoachPrepare>();
        
        if(coaches.isEmpty()) {
            throw new BusinessException("当前所选条件下无可选教练");
        }
        
        List<String> hours = scheduleDaoSupport.getScheduleObligatoryHours(hour, experience);
        List<Long> startGoingCoachIds = scheduleDaoSupport.listOnGoingCoachIds(day, hours.subList(0, 1));
        List<Long> onGoingCoachIds = scheduleDaoSupport.listOnGoingCoachIds(day, hours.subList(1, 3)); 
        
        Map<String, List<Long>> allRestCoachIds = scheduleDaoSupport.filterRestCoachIds(day, hour, experience);
        
        List<Long> restCoachIds = allRestCoachIds.get("rest");
        
        List<Long> experienceCoachIds = allRestCoachIds.get("experience");
        
//        List<Long> dutyCoachIds = allRestCoachIds.get("duty");
        
        List<Long> businessCoachIds = allRestCoachIds.get("business");
        
        List<Long> annualLeaveCoachIds = allRestCoachIds.get("annual-leave");
        
        List<Long> conferenceCoachIds = allRestCoachIds.get("conference");
        
        List<Long> trainingCoachIds = allRestCoachIds.get("training");
        
        for(Principal coach : coaches) {
            Long coachId = coach.getId();
            if(startGoingCoachIds.contains(coachId)) {
            	fiterCoaches.add(new CoachPrepare(coach, "上课中"));
            }else if(onGoingCoachIds.contains(coachId)){ 
            	fiterCoaches.add(new CoachPrepare(coach, "被占用"));
            }else if (restCoachIds != null && restCoachIds.size() != 0 && restCoachIds.contains(coachId)) {
                fiterCoaches.add(new CoachPrepare(coach, "繁忙-休息"));
            } else if (experienceCoachIds != null && experienceCoachIds.size() != 0 && experienceCoachIds.contains(coachId)) {
                fiterCoaches.add(new CoachPrepare(coach, "繁忙-体验课"));
            } else if (businessCoachIds != null && businessCoachIds.size() != 0 && businessCoachIds.contains(coachId)) {
                fiterCoaches.add(new CoachPrepare(coach, "繁忙-公出"));
            } else if (annualLeaveCoachIds != null && annualLeaveCoachIds.size() != 0 && annualLeaveCoachIds.contains(coachId)) {
                fiterCoaches.add(new CoachPrepare(coach, "繁忙-年假"));
            } else if(conferenceCoachIds != null && conferenceCoachIds.size() != 0 && conferenceCoachIds.contains(coachId)) {
                fiterCoaches.add(new CoachPrepare(coach, "繁忙-会议"));
            } else if(trainingCoachIds != null && trainingCoachIds.size() != 0 && trainingCoachIds.contains(coachId)) {
                fiterCoaches.add(new CoachPrepare(coach, "繁忙-培训"));
            } else {
                fiterCoaches.add(new CoachPrepare(coach, "可预约"));
            }
        }
            
        return fiterCoaches;
        
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#genereateYearCoach(co.fitstart.entity.site.Site, java.util.Date, java.lang.String, boolean)
     */
    public List<CoachPrepare> genereateYearCoach(Site site, Date day, String hour, boolean experience) {
        
        List<Principal> coaches = scheduleDaoSupport.listCoach(site, day);
        
        List<CoachPrepare> filterCoaches = this.filterCoaches(coaches, day, hour, experience);
        
        return filterCoaches;
    }
    
    
    /**
     * 
     * @param courseRound
     * @param site
     * @param coach
     * @param roomType
     * @param day
     * @param hour
     */
    public void appointmentSchedule(CourseRound courseRound, Site site, Principal coach, RoomType roomType, Date day, String hour) {
        
        if(courseRound == null) {
            throw new BusinessException("约课参数错误");
        }
        
        if(site == null) {
            throw new BusinessException("约课参数错误");
        }
        
        if(coach == null || !coach.isCoach()) {
            throw new BusinessException("约课参数错误");
        }
        
        // send message to student
        String studentMobile = courseRound.getStudent().getMobile();
        String studentName = courseRound.getStudent().getName();
        int remain = site.getLimit(roomType);
        
        boolean experience = (courseRound.getCourseType().isIndividual() && courseRound.getCourse().isExperience());
        
        // validate room remain
        remain = remain - scheduleDaoSupport.countUsingRoom(site, roomType, day, hour, experience);
        if(remain <= 0) {
            throw new BusinessException("当前区域已预约满，请选择其他区域");
        }
       
        RoomScheduleRecord roomScheduleRecord = new RoomScheduleRecord(site, courseRound.getCode(), day, hour, roomType, 1);
        genericDaoSupport.save(roomScheduleRecord);
        
        CourseSchedule courseSchedule = null;
        CourseType courseType = courseRound.getCourseType();
        
        if(courseType.isYear()) {
            // create new courseSchedule
            courseSchedule = new CourseSchedule(courseRound, site, coach, day, hour);
            genericDaoSupport.save(courseSchedule.roomScheduleRecord(roomScheduleRecord));
        }else if(courseType.isIndividual()) {
            courseSchedule = scheduleDaoSupport.loadCourseSchedule(courseRound, ScheduleStatus.SCHEDULE);
            if(courseSchedule != null) {
                genericDaoSupport.update(courseSchedule.editTime(day, hour).changeStatus(ScheduleStatus.READY).roomScheduleRecord(roomScheduleRecord).editCoach(coach).editSite(site));
            }
            
            if(courseRound.getCourse().isExperience()) {
                scheduleDaoSupport.createCoachRestCalendar(coach, day, hour, CoachRestType.EXPERIENCE);
            }
        }
        
        // if this schedule is first schedule
        RoundStatus roundStatus = courseRound.getCourseStatus();
        Course course = courseRound.getCourse();
        if(roundStatus.isPrepare()) {
            genericDaoSupport.update(courseRound.readyCourse().initPeriod(courseSchedule.getOccurDate()));
            genericDaoSupport.update(courseRound.getOrder().orderStatus(OrderStatus.PROCESSING));
            
            if(courseType.isIndividual() || courseType.isYear()) {
                if(course.isExperience()) {
                    smsSender.sendExperienceRoundNotice(studentMobile, studentName, courseSchedule.getDayhour(), coach.getName(), coach.getMobile());
                }
                smsSender.sendIndividualRoundNotice(studentMobile, courseSchedule.getDayhour(), courseSchedule.getRoomType().getDescription());
            } 
            
        }
        
        CoachStudentRelation coachStudentRelation = scheduleDaoSupport.loadCoachStudentRelation(coach, courseRound.getStudent(), courseType);
        if(coachStudentRelation == null) {
            coachStudentRelation = new CoachStudentRelation(coach, courseSchedule);
        }
        genericDaoSupport.saveOrUpdate(coachStudentRelation.editSchedule(courseSchedule));
        
        scheduleDaoSupport.createScheduleSmsNotice(courseSchedule);
        
        // create new principal message
        String message = "您有新的单节预约课程，课程时间为【"+ DateFormatUtils.format(day, "yyyy-MM-dd") + " " + hour +"】，学员为【" + courseRound.getStudent().getName() + "】，主教练为【" + courseRound.getCoach().getName() + "】，上课地点【"+courseRound.getSiteName()+"】，请准时去上课。";
        
        PrincipalMessage principalMessage = new PrincipalMessage(coach);
        genericDaoSupport.save(principalMessage.initialize("新课程预约", "您有新的单节预约课程", message));
        
        tencentHandler.sendMessage(message, coach.getMobile());
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#changeSchedule(java.lang.Long, co.fitstart.entity.principal.Principal, co.fitstart.entity.site.RoomType, co.fitstart.entity.site.Site, java.util.Date, java.lang.String)
     */
    public void changeSchedule(Long scheduleId, Principal coach, RoomType roomType, Site site, Date day, String hour) {
        CourseSchedule courseSchedule = genericDaoSupport.load(CourseSchedule.class, scheduleId);
        
        int remain = site.getLimit(roomType);
        
        boolean experience = (courseSchedule.getCourseType().isIndividual() && courseSchedule.getCourse().isExperience());
        
        // validate room remain
        remain = remain - scheduleDaoSupport.countUsingRoom(site, roomType, day, hour, experience);
        if(remain <= 0) {
            throw new BusinessException("当前区域已预约满，请选择其他区域");
        }
        
        RoomScheduleRecord roomScheduleRecord = courseSchedule.getRoomSchedule();
        if(roomScheduleRecord != null) {
            genericDaoSupport.update(roomScheduleRecord.updateTime(day, hour, site));
        }else {
            String roundCode = courseSchedule.getCourseRound().getCode();
            roomScheduleRecord = new RoomScheduleRecord(site, roundCode, day, hour, roomType, 1);
            genericDaoSupport.save(roomScheduleRecord);
        }
        
        // make message before update
        Principal cancelCoach = courseSchedule.getCoach();
        if(cancelCoach != coach) {
            String message = "您的学员" + courseSchedule.getStudent().getName() + "，有课程取消："
                    + DateFormatUtils.format(courseSchedule.getOccurDate()) + " " + courseSchedule.getOccurHour() 
                    + "已被学员取消"
                    + "，请去系统查看。";
            PrincipalMessage cancelMessage = new PrincipalMessage(cancelCoach);
            genericDaoSupport.save(cancelMessage.initialize("课程变更", "您有新的课程变更信息", message));
            
            tencentHandler.sendMessage(message, cancelCoach.getMobile());
        }
        
        //record schedule change
        if(courseSchedule.getCourseType().isYear() || (courseSchedule.getCourseType().isIndividual() && !courseSchedule.getCourse().isExperience())) {
            if(DateUtils.isSameDay(new Date(), courseSchedule.getOccurDate()) && !DateUtils.isSameDay(new Date(), day)) {
                CourseScheduleCancelRecord record = scheduleDaoSupport.loadScheduleCancelRecord(courseSchedule);
                
                if(record == null) {
                    record = new CourseScheduleCancelRecord(courseSchedule, false);
                    
                    genericDaoSupport.save(record);
                }
            }
        }
        
        if(courseSchedule.getCourse().isExperience()) {
            Date canceledDayhour = DateUtils.parseDate(DateFormatUtils.format(courseSchedule.getOccurDate(), "yyyy-MM-dd ") + courseSchedule.getOccurHour(), "yyyy-MM-dd HH:mm");
            String canceledRestHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(canceledDayhour, 1), "HH:mm");
            CoachRestCalendar calendar = scheduleDaoSupport.loadCoachRestCalendar(cancelCoach, courseSchedule.getOccurDate(), canceledRestHour, CoachRestType.EXPERIENCE);
            
            Date dayhour = DateUtils.parseDate(DateFormatUtils.format(day, "yyyy-MM-dd ") + hour, "yyyy-MM-dd HH:mm");
            String restHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(dayhour, 1), "HH:mm");
            
            if(calendar == null) {
                scheduleDaoSupport.createCoachRestCalendar(coach, dayhour, restHour, CoachRestType.EXPERIENCE);
            } else {
                genericDaoSupport.update(calendar.edit(coach, dayhour, restHour).editAdminExperience(false));
            }
        }
        
        scheduleDaoSupport.disableScheduleSmsNotice(courseSchedule);
        
        // edit course schedule
        courseSchedule.editCoach(coach)
                      .editTime(day, hour)
                      .editSite(site)
                      .roomScheduleRecord(roomScheduleRecord);
        genericDaoSupport.update(courseSchedule);
        
        //make message after update
        if(cancelCoach != coach) {
            String message = "您有新的单节课程，学员为【" + courseSchedule.getStudent().getName() + "】，上课时间为【"
                    + courseSchedule.getDayhour() 
                    +"】，主教练为【" + courseSchedule.getCourseRound().getCoach().getName() + "】，上课地点【"+courseSchedule.getCourseRound().getSiteName()+"】，请在我的学员点击查看，并及时上课。";
            PrincipalMessage newMessage = new PrincipalMessage(courseSchedule.getCoach());
            genericDaoSupport.save(newMessage.initialize("新单节课程", "您有新的单节课程", message));
            
            tencentHandler.sendMessage(message, coach.getMobile());
        } else {
            String message = "您有新的单节课程时间已被修改，学员为【" + courseSchedule.getStudent().getName() + "】，上课时间为【"
                    + courseSchedule.getDayhour() 
                    +"】，主教练为【" + courseSchedule.getCourseRound().getCoach().getName() + "】，上课地点【"+courseSchedule.getCourseRound().getSiteName()+"】，请在我的学员点击查看，并及时上课。";
            PrincipalMessage updateMessage = new PrincipalMessage(coach);
            genericDaoSupport.save(updateMessage.initialize("单节课程改课时间", "您有新的单节课程时间", message));
            
            tencentHandler.sendMessage(message, coach.getMobile());
        }
        
        scheduleDaoSupport.createScheduleSmsNotice(courseSchedule);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.mobile.service.AppointmentService#listSite(co.fitstart.entity.principal.Principal, java.util.Date)
     */
    public List<Site> listSite(Principal coach, Date day) {
        return scheduleDaoSupport.listSite(coach, day);
    }
}
